/////////////////////////////////////////////////////////////////////////////////////
// Header Protection
#ifndef _RENDERSTATE_H_
#define _RENDERSTATE_H_

/////////////////////////////////////////////////////////////////////////////////////
// Include Files

// TODO: should these be here? reorganize? idk...
#include "Rendering\GLExt.h"
//#include "GLee\GLee.h"

#include "Cg\CgGL.h"

/* TODO
 * after I am done changing to pre-loading all assets, all "fixing up" by string 
 * searching for named assets in their managers is happening every frame. I should 
 * change this to find them when they are loaded instead.
 */

/////////////////////////////////////////////////////////////////////////////////////
// Forward Declarations

// managers
class CTextureManager;
class CShaderManager;
class CMeshManager;

enum param_e
{
	PARAM_MAT_MODELVIEW, 
	PARAM_MAT_MODELVIEWPROJ, 
	PARAM_MAT_MODELVIEW_ROT, 
	PARAM_MAT_MODELVIEWPROJ_ROT, 
};

/////////////////////////////////////////////////////////////////////////////////////
// This class provides the communication between an object and the renderer. 
//     Each time the renderer updates it will render every registered render state.
class CRenderState
{
private:    // structures
	// TODO: TTexParam, TMatParam, w/e other params there are
	struct TTexture
	{
		string_t strName;
		string_t strParam;
	};

	struct TMatrix
	{
		string_t strParam;
		param_e  eType;
	};

public:     // methods

	// TODO: it will obviously be a problem to have to overload new in every 
	//     dynamically allocated class which uses a vector or matrix, I will have to 
	//     do something about this for sure... 
	// TEMP: overloading new in every dynamically allocated class that uses __m128
	// LATER: overload new globally to take in an alignment
	// AWESOME: memory manager automatically knows to align it
	void *operator new( uint_t _uiBytes )
	{
		// allocate memory with some extra
		byte_t *pUnaligned = (byte_t *)malloc( _uiBytes + 16 );

		// move the pointer over at least one and align it
		byte_t *pAligned = pUnaligned + (16 - uint_t(pUnaligned) % 16);

		// add the offset into the previous byte
		pAligned[-1] = byte_t(pAligned - pUnaligned);

		// return the aligned memory
		return pAligned;
	}

	void operator delete(void *pAligned)
	{
		// find the amount this pointer was offset for alignment
		byte_t uiOffset = ((byte_t *)pAligned)[-1];

		// move the pointer
		byte_t *pUnaligned = (byte_t *)pAligned - uiOffset;

		// free the memory
		free(pUnaligned);
	}

	CRenderState();

	~CRenderState() { }

	void Lock()   { m_Mutex.Lock(); }
	void Unlock() { m_Mutex.Unlock(); }

	void Render( matrix_t const &_matView, matrix_t const &_matProj );

	matrix_t GetTransform()
	{
		return m_matTransform;
	}

	void SetTransform( matrix_t const &_matTransform )
	{
		m_matTransform = _matTransform;
	}

	void SetMatrixParam( string_t _strParam, param_e _eParam )
	{
		// create the matrix param
		TMatrix mat;
		mat.strParam = _strParam;
		mat.eType    = _eParam;
		m_lstMatParams.Append(mat);
	}

	void AddTexParam( string_t _strName, string_t _strParam )
	{
		// create the texture param
		TTexture tex;
		tex.strName  = _strName;
		tex.strParam = _strParam;
		m_lstTexParams.Append(tex);
	}

	void SetShader( string_t _strShader )
	{
		m_strShader = _strShader;
	}

	void SetMesh( string_t _strMesh )
	{
		m_strMesh = _strMesh;
	}

	void AddRef()
	{
		m_uRefCount++;
	}

	void Release()
	{
		if( --m_uRefCount == 0 )
		{
			delete this;
		}
	}

	string_t GetShaderName() { return m_strShader; }

private:
	// managers
	CSingleton<CTextureManager> m_pTextureManager;
	CSingleton<CShaderManager>  m_pShaderManager;
	CSingleton<CMeshManager>    m_pMeshManager;

	uint_t m_uRefCount;

	mutex_t  m_Mutex;
	matrix_t m_matTransform;

	// mesh
	string_t m_strMesh;

	// shader
	string_t m_strShader;

	list_t<TTexture> m_lstTexParams;
	list_t<TMatrix>  m_lstMatParams;
};

class CRenderStateEventArg : public CEventArg
{
public:     // methods

	CRenderStateEventArg(CRenderState *_pRenderState) : 
			CEventArg(), m_pRenderState(_pRenderState)
			{
				m_pRenderState->AddRef();
			}

			virtual ~CRenderStateEventArg()
			{
				m_pRenderState->Release();
			}

			CRenderState *GetRenderState() { return m_pRenderState; }

private:    // data
	CRenderState *m_pRenderState;

private:    // dummy definitions
	CRenderStateEventArg( CRenderStateEventArg const & );
	CRenderStateEventArg &operator =( CRenderStateEventArg const & );
};

/////////////////////////////////////////////////////////////////////////////////////
// Header Protection
#endif // _RENDERSTATE_H_
